11  Pandas 数据框内部操作

11.1 引言数据框操作的数据清洗价值

数据清洗(Data Cleaning)是数据分析中最耗时的环节,通常占据60-80%的时间。Pandas提供了丰富的数据框内部操作方法,使得数据清洗工作变得高效而可靠。

理论背景:数据质量原则

高质量数据分析的前提是高质量数据。数据清洗的目标: 1. 完整性: 无缺失值或合理处理 2. 一致性: 格式统一,单位一致 3. 准确性: 值在合理范围内 4. 唯一性: 无重复记录

11.2 数据读取与基础操作

11.2.1 read_csv/read_excel参数详解

列表 11.1
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import pandas as pd  # 导入Pandas数据分析库
#将第一列(date)设置为索引,只读取指定的列usecols
df=pd.read_csv("https://huoran.oss-cn-shenzhen.aliyuncs.com/20230306/csv/1632645776084066304.csv",index_col=0,usecols=['date','syzg','zgpa','zgjz']).dropna()

Trade_days=df.shape[0]  # 获取数据维度信息
print(f'2022年共有{Trade_days}个交易日')  # 输出2022年共有

stock_price=df.loc['2022-03-01','zgjz']  # 按标签索引提取数据
print(f'3月第一个交易日中国建筑价格为{stock_price}元')  # 输出3月第一个交易日中国建筑价格为

df_asc=df.sort_index(ascending=True)  # 按索引排序
print(df_asc.head(8))  # 输出前几行数据

代码深度解析:

  1. index_col的作用:

    • 将某列设为行索引(通常是日期)
    • 便于时间序列分析
    • 减少内存占用(索引不重复存储)
  2. usecols的优势:

    • 内存效率: 只加载需要的列
    • 读取速度: 减少I/O操作
    • 代码清晰: 明确分析目标
  3. 链式操作:

    df = (pd.read_csv(...)
           .dropna()
           .sort_index())

11.2.2 数据框基础属性

列表 11.2
# =============================================================================
# 题目:查看DataFrame的基本属性和结构信息
# =============================================================================
# 本代码块演示如何快速了解DataFrame的基本情况。
# 金融应用场景:数据探索性分析(EDA),检查数据质量,确认数据覆盖范围。
# =============================================================================

# ==================== 数据框形状 ====================
print(f"数据框形状: {df.shape}")  # 返回元组(行数, 列数)
print(f"  - 行数(交易日): {df.shape[0]}")  # 交易日数量
print(f"  - 列数(股票): {df.shape[1]}")  # 股票数量

# ==================== 交易日数量 ====================
Trade_days = df.shape[0]  # 保存交易日数量到变量
print(f"\n2022年共有{Trade_days}个交易日")  # 显示交易日统计

# ==================== 索引信息 ====================
print(f"\n索引类型: {type(df.index)}")  # 显示索引类型(DatetimeIndex)
print(f"索引范围: {df.index.min()}{df.index.max()}")  # 显示日期范围
# 输出解释:显示数据涵盖的起始日期和结束日期

# ==================== 列名 ====================
print(f"\n列名(股票代码):")
print(df.columns.tolist())  # 将列名转换为列表显示
# 输出解释:syzg(三一重工)、zgpa(中国平安)、zgjz(中国建筑)

# ==================== 数据类型 ====================
print(f"\n数据类型:")
print(df.dtypes)  # 显示每列的数据类型
# 输出解释:所有列都是float64类型,表示价格数据

# ==================== 内存使用 ====================
print(f"\n内存使用:")
print(df.memory_usage(deep=True))  # 显示每列的内存占用
# 输出解释:deep=True深入计算字符串等对象的内存使用

11.3 数据选择与索引

11.3.1 loc基于标签的索引

loc使用标签(行/列名)进行选择,包含末端

列表 11.3
# =============================================================================
# 题目:使用loc基于标签选择数据
# =============================================================================
# 本代码块演示如何使用loc方法根据标签(日期、列名)选择数据。
# 金融应用场景:查询特定日期的股价,提取特定日期范围的数据,条件筛选。
# =============================================================================

# ==================== 获取特定日期的单个值 ====================
stock_price = df.loc['2022-03-01', 'zgjz']  # 使用loc选择特定行和列的交叉点
print(f"3月第一个交易日中国建筑价格: {stock_price}元")
# 输出解释:显示2022-03-01这一天zgjz(中国建筑)的收盘价

# ==================== 选择多行多列(切片) ====================
subset = df.loc[  # 使用loc选择数据子集
    '2022-03-01':'2022-03-05',  # 行切片:日期范围(包含两端)
    ['syzg', 'zgpa']  # 列选择:指定列名列表
]
print(f"\n3月1-5日,三一重工和中国平安的价格:")
print(subset)
# 输出解释:显示3月1日至5日(共5行),syzg和zgpa两列的数据
# 注意:loc的标签切片包含两端(与Python切片不同)

# ==================== 布尔条件(条件筛选) ====================
print(f"\n三一重工价格>20的交易日:")
syzg_high = df.loc[df.syzg > 20]  # 使用布尔条件选择行
print(syzg_high)
# 输出解释:显示所有syzg列价格大于20的行
# df.syzg > 20返回布尔Series,loc用它来筛选行

11.3.2 iloc基于位置的索引

iloc使用整数位置进行选择,不包含末端

列表 11.4
# =============================================================================
# 题目:使用iloc基于整数位置选择数据
# =============================================================================
# 本代码块演示如何使用iloc方法根据整数位置选择数据。
# 金融应用场景:获取前N行数据,跳过表头,随机采样,批量处理。
# =============================================================================

# ==================== 前5行前2列 ====================
print("前5行前2列:")
print(df.iloc[:5, :2])  # 行位置0-4,列位置0-1
# 输出解释:显示前5个交易日,前2列股票的价格
# 注意:iloc的切片不包含末端(标准Python切片规则)

# ==================== 特定位置的单个值 ====================
print(f"\n第1行第1列: {df.iloc[0, 0]}")  # 第1行(位置0),第1列(位置0)
# 输出解释:显示第1个交易日,第1列股票的价格

# ==================== 负索引(倒数) ====================
print(f"\n倒数第3行:")
print(df.iloc[-3])  # 倒数第3行(所有列)
# 输出解释:显示倒数第3个交易日的所有股票价格
# 负数从末尾开始计数:-1是最后1行,-2是倒数第2行

11.4 数据排序

11.4.1 sort_index按索引排序

列表 11.5
# =============================================================================
# 题目:按索引(日期)排序DataFrame
# =============================================================================
# 本代码块演示如何按行索引排序数据。
# 金融应用场景:确保时间序列数据按时间顺序排列,这是时间序列分析的前提。
# =============================================================================

# ==================== 升序排序(默认) ====================
df_asc = df.sort_index(ascending=True)  # 按索引升序排序(早到晚)
print("升序排序(默认):")
print(df_asc.head(8))  # 显示前8行
# 输出解释:数据按日期从早到晚排列(2022-01-04在最前)

# ==================== 降序排序 ====================
df_desc = df.sort_index(ascending=False)  # 按索引降序排序(晚到早)
print(f"\n降序排序:")
print(df_desc.head(3))  # 显示前3行(实际是最晚的3天)
# 输出解释:数据按日期从晚到早排列(最新的日期在最前)

金融应用: 确保时间序列数据按时间顺序排列

11.4.2 sort_values按值排序

列表 11.6
# =============================================================================
# 题目:按列值排序DataFrame
# =============================================================================
# 本代码块演示如何按某列或多列的值排序。
# 金融应用场景:找出股价最高/最低的交易日,多维度排序分析。
# =============================================================================

# ==================== 按单列排序 ====================
print("按三一重工价格降序:")
df_sorted = df.sort_values('syzg', ascending=False)  # 按syzg列降序排序
print(df_sorted.head())  # 显示前几行
# 输出解释:syzg价格最高的交易日排在最前面

# ==================== 按多列排序 ====================
print(f"\n先按syzg升序,再按zgpa降序:")
df_multi_sort = df.sort_values(  # 按多列排序
    ['syzg', 'zgpa'],  # 排序依据的列
    ascending=[True, False]  # 分别指定排序方向
)
print(df_multi_sort.head())
# 输出解释:
# - 先按syzg升序(低到高)
# - syzg相同时,按zgpa降序(高到低)
# - 适用于多层级排序需求

11.5 数据筛选与查询

11.5.1 条件筛选

列表 11.7
# =============================================================================
# 题目:使用条件表达式筛选DataFrame数据
# =============================================================================
# 本代码块演示如何使用单个或多个条件筛选数据。
# 金融应用场景:找出股价突破关键点的交易日,多指标联合筛选。
# =============================================================================

# ==================== 单条件筛选 ====================
print("三一重工>20:")
print(df[df.syzg > 20].head())  # 使用布尔条件筛选
# 输出解释:显示syzg>20的所有行(前几行)
# df.syzg > 20返回布尔Series,用于筛选行

# ==================== 多条件(and) ====================
print(f"\n三一重工>20 且 中国平安>40:")
print(df[(df.syzg > 20) & (df.zgpa > 40)].head())
# 输出解释:显示同时满足两个条件的行
# &表示逻辑与(AND),两边条件必须同时满足
# 注意:每个条件必须用括号括起来

# ==================== 多条件(or) ====================
print(f"\n三一重工>30 或 中国平安>45:")
print(df[(df.syzg > 30) | (df.zgpa > 45)].head())
# 输出解释:显示满足至少一个条件的行
# |表示逻辑或(OR),两边条件满足一个即可

# ==================== 使用isin方法(成员检查) ====================
print(f"\n特定日期的数据:")
dates_to_check = ['2022-03-01', '2022-03-02', '2022-03-03']  # 日期列表
print(df.loc[dates_to_check])  # 选择索引在列表中的行
# 输出解释:显示这3个特定日期的数据
# 等价于df[df.index.isin(dates_to_check)]

11.5.2 query方法

query方法提供了更直观的查询语法:

列表 11.8
# =============================================================================
# 题目:使用query方法进行更直观的数据查询
# =============================================================================
# 本代码块演示query方法,它提供了类似SQL的查询语法。
# 金融应用场景:编写复杂的多条件查询,提高代码可读性。
# =============================================================================

# ==================== 简单条件 ====================
result1 = df.query('syzg > 20')  # 使用query方法筛选
print("syzg > 20:")
print(result1.head())
# 输出解释:与df[df.syzg > 20]结果相同
# 优势:语法更直观,不需要重复写df前缀

# ==================== 多条件 ====================
result2 = df.query('syzg > 20 and zgpa < 40')  # 多条件查询
print(f"\nsyzg > 20 and zgpa < 40:")
print(result2.head())
# 输出解释:显示同时满足两个条件的行
# 优势:使用and/or/not代替&/|/~,更符合自然语言

# ==================== 引用变量 ====================
threshold = 25  # 定义阈值变量
result3 = df.query('syzg > @threshold')  # 使用@引用外部变量
print(f"\nsyzg > {threshold}:")
print(result3.head())
# 输出解释:显示syzg > 25的行
# @符号告诉query在Python环境中查找变量,而不是列名

11.6 数据变换

11.6.1 apply与map

列表 11.9
# =============================================================================
# 题目:使用apply和map方法进行数据变换
# =============================================================================
# 本代码块演示如何对DataFrame和Series应用自定义函数。
# 金融应用场景:计算价格区间、分类评级、应用复杂业务逻辑。
# =============================================================================

# ==================== apply:对行或列应用函数 ====================
print("每列的极差:")
print(df.apply(lambda x: x.max() - x.min()))  # 对每列应用lambda函数
# 输出解释:显示每列的最大值与最小值之差(价格波动范围)
# apply默认对列操作(axis=0)

print(f"\n每行的均值:")
print(df.apply(lambda x: x.mean(), axis=1).head())  # 对每行应用函数
# 输出解释:显示每个交易日3只股票的平均价格
# axis=1表示沿行方向操作(跨列)

# ==================== map:对Series的每个元素应用函数 ====================
print(f"\n将价格分类:")

# ------------------- 定义分类函数 -------------------
def categorize_price(price):  # 定义价格分类函数
    """
    根据价格将股票分类为高、中、低三档
    参数:
        price: 股票价格
    返回:
        '高'/'中'/'低': 价格等级
    """
    if price > 30:  # 价格大于30
        return '高'  # 高价股
    elif price > 20:  # 价格在20-30之间
        return '中'  # 中价股
    else:  # 价格小于等于20
        return '低'  # 低价股

# ------------------- 应用分类函数 -------------------
syzg_category = df['syzg'].map(categorize_price)  # 对syzg列的每个元素应用函数
print(syzg_category.head(10))  # 显示前10个分类结果
# 输出解释:每个价格被转换为'高'/'中'/'低'三类之一
# map只用于Series,对每个元素逐个应用函数